/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*   File:      Logger.cpp
*   Author:    刘洋兵
*   Function:  日志实现功能实现类
*   Time:      2025-09-09
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include "Logger.h"

Logger* Logger::m_instance = nullptr;

/**
 * @brief  日志类初始化构造函数
 * @param  parent 父窗口
 */
Logger::Logger(QObject* parent) : QObject(parent),
    m_minLevel(LogLevel::DEBUG),
    m_consoleOutput(true),
    m_fileOutput(false)
{
    QString appName = QCoreApplication::applicationName();
    if(appName.isEmpty()) {
        appName = "Logger";
    }
    QString time = QDateTime::currentDateTime().toString("yyyy_MM_dd");
    m_logFileName = appName + ".log";
    m_logFileName = QString("%1_%2.log").arg(appName).arg(time);
}

/**
 * @brief  日志类初始化析构函数
 */
Logger::~Logger()
{
    if(m_logFile.isOpen()) {
        m_logFile.close();
    }
}

/**
 * @brief 获取日志类单例实例
 * @return Logger实例指针
 */
Logger* Logger::getInstance()
{
    static QMutex mutex;
    QMutexLocker locker(&mutex);
    if(!m_instance) {
        m_instance = new Logger();
    }
    return m_instance;
}

/**
 * @brief  初始化相关参数
 * @param  logDir 设置log文件夹名
 * @param  minLevel 输出类型索引
 * @param  consoleOutput 是否打印台输出
 */
void Logger::init(const QString& logDir, LogLevel minLevel, bool consoleOutput)
{
    QMutexLocker locker(&m_mutex);

    m_minLevel = minLevel;
    m_consoleOutput = consoleOutput;
    QString time = QDateTime::currentDateTime().toString("yyyy_MM_dd");

    if(!logDir.isEmpty()) {
        QDir dir(logDir);
        dir = dir.filePath(time);
        if(!dir.exists()) {
            dir.mkpath(".");
        }
        m_logFileName = dir.filePath(m_logFileName);
    }

    m_logFile.setFileName(m_logFileName);
    if(m_logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
        m_textStream.setDevice(&m_logFile);
        m_fileOutput = true;
    } else {
        qDebug() << "Failed to open log file:" << m_logFileName;
        m_fileOutput = false;
    }
}

/**
 * @brief  便捷方法的实现类似
 */
void Logger::debug(const QString& message, const QString& file,
                  int line, const QString& function)
{
    log(LogLevel::DEBUG, message, file, line, function);
}

void Logger::info(const QString &message, const QString &file, int line, const QString &function)
{
    log(LogLevel::INFO, message, file, line, function);
}

void Logger::warning(const QString &message, const QString &file, int line, const QString &function)
{
    log(LogLevel::WARNING, message, file, line, function);
}

void Logger::error(const QString &message, const QString &file, int line, const QString &function)
{
    log(LogLevel::ERR, message, file, line, function);
}

void Logger::critical(const QString &message, const QString &file, int line, const QString &function)
{
    log(LogLevel::CRITICAL, message, file, line, function);
}

// ... ............ ...

/**
 * @brief  获取对应输出类型
 * @param  level 输出类型索引
 * @return 应输出类型字符串
 */
QString Logger::levelToString(LogLevel level) const
{
    switch(level) {
    case LogLevel::DEBUG:    return "DEBUG";
    case LogLevel::INFO:     return "INFO";
    case LogLevel::WARNING:  return "WARNING";
    case LogLevel::ERR:      return "ERROR";
    case LogLevel::CRITICAL: return "CRITICAL";
    default:                 return "UNKNOWN";
    }
}

/**
 * @brief  拼接含日期、输出类型等日志输出数据
 * @param  level 输出类型索引
 * @param  message 输出的日志数据
 * @param  file 输出的日志文件路径
 * @param  line 输出命令所在文件行数
 * @param  function 输出命令所在文件函数名
 */
void Logger::log(LogLevel level, const QString& message,
                const QString& file, int line, const QString& function)
{
    if(level < m_minLevel) {
        return;
    }

    QString formatted = formatMessage(level, message, file, line, function);

    QMutexLocker locker(&m_mutex);

    //输出到控制台
    if(m_consoleOutput) {
        QTextStream out(stdout);

        out <<formatted << endl;
    }

    if(m_fileOutput && m_logFile.isOpen()) {
        m_textStream << formatted << endl;
        m_textStream.flush();
    }

    emit logMessage(formatted, level);
}

/**
 * @brief  封装输出数据
 * @param  level 输出类型索引
 * @param  message 输出的日志数据
 * @param  file 输出的日志文件路径
 * @param  line 输出日志所在的文件行数
 * @param  function 输出命令所在文件函数名
 * @return 返回输出的日志字符串
 */
QString Logger::formatMessage(LogLevel level, const QString& message,
                            const QString& file, int line,
                            const QString& function) const
{
    QString time = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz");
    QString levelStr = levelToString(level);

    QString formatted = QString("[%1] [%2] %3")
                       .arg(time)
                       .arg(levelStr)
                       .arg(message);

    if(!file.isEmpty()) {
        QString shortFile = file.mid(file.lastIndexOf('/') + 1);
        formatted += QString(" (%1:%2").arg(shortFile).arg(line);
        if(!function.isEmpty()) {
            formatted += QString(", %1").arg(function);
        }
        formatted += ")";
    }

    return formatted;
}

/**
 * @brief  设置输出索引的最小值
 * @param  level 输出类型索引
 */
void Logger::setMinLevel(LogLevel level)
{
    QMutexLocker locker(&m_mutex);
    m_minLevel = level;
}

/**
 * @brief  日志文件备份
 */
void Logger::rotateLog()
{
    if(!m_fileOutput || !m_logFile.isOpen()) {
        return;
    }

    if(m_logFile.size() < m_maxFileSize) {
        return;
    }

    m_logFile.close();

    // 重命名现有备份文件
    for(int i = m_maxBackupCount - 1; i > 0; --i) {
        QString oldName = QString("%1.%2").arg(m_logFileName).arg(i);
        QString newName = QString("%1.%2").arg(m_logFileName).arg(i + 1);
        QFile::remove(newName);
        QFile::rename(oldName, newName);
    }

    // 重命名当前日志文件
    QString newName = QString("%1.1").arg(m_logFileName);
    QFile::remove(newName);
    QFile::rename(m_logFileName, newName);

    // 重新打开日志文件
    if(m_logFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
        m_textStream.setDevice(&m_logFile);
    } else {
        m_fileOutput = false;
        qWarning() << "Failed to reopen log file after rotation";
    }
}

/**
 * @brief  设置日志文件最大内存
 */
void Logger::setMaxFileSize(qint64 size)
{
    QMutexLocker locker(&m_mutex);
    m_maxFileSize = size;
}

/**
 * @brief  设置日志文件最大备份数
 */
void Logger::setMaxBackupCount(int count)
{
    QMutexLocker locker(&m_mutex);
    m_maxBackupCount = qMax(1, count);
}
